package post

import (
	"context"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/utils/pointy"
	"mall/service/forum/model"
	"mall/service/forum/rpc/internal/svc"
	"mall/service/forum/rpc/internal/utils"
	"mall/service/forum/rpc/internal/utils/errorhandler"
	"mall/service/forum/rpc/types/forum"

	"github.com/zeromicro/go-zero/core/logx"
)

type BackendGetPostListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewBackendGetPostListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BackendGetPostListLogic {
	return &BackendGetPostListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *BackendGetPostListLogic) BackendGetPostList(in *forum.BackendPostListReq) (*forum.BackendPostListResp, error) {
	var orderBys []string
	if len(in.OrderBy) > 0 {
		for _, orderBy := range in.OrderBy {
			order := " ASC"
			if orderBy.Order == "1" {
				order = " DESC"
			}
			if orderBy.Field == "id" {
				orderBys = append(orderBys, model.ForumPostColumns.PostID+order)
			}
			if orderBy.Field == "sort" {
				orderBys = append(orderBys, model.ForumPostColumns.Sort+order)
			}
			if orderBy.Field == "createdAt" {
				orderBys = append(orderBys, model.ForumPostColumns.PostDate+order)
			}
		}
	}

	var stati string
	var postType string
	if len(in.FilterBy) > 0 {
		for _, filterBy := range in.FilterBy {
			switch filterBy.Field {
			case "status":
				stati = filterBy.In
			case "type":
				postType = filterBy.In
			}
		}
	}

	var page uint64
	var pageSize uint64
	var restricted bool
	var withCount int
	var preCount int64
	filterMap := make(map[string]map[string][]any)
	resp := &forum.BackendPostListResp{}
	if in.Ids != nil && len(*in.Ids) > 0 {
		if vals := modelx.UniqueInterfaces(*in.Ids, 64); len(vals) == 0 {
			return resp, nil
		} else {
			filterMap[model.ForumPostColumns.PostID] = map[string][]any{
				"in": vals,
			}
			restricted = true
			withCount = -1
			preCount = int64(len(vals))
		}
	}

	var statusIsSet bool
	var isAll bool
	statusMap := make(map[string][]any)
	switch postType {
	case "1": // 全部
		statusMap["notEqualTo"] = []any{globalkey.StatusDeleted}
		isAll = true
	case "2": // 待审核
		filterMap[model.ForumPostColumns.PostStatus] = map[string][]any{
			"equalTo": {globalkey.StatusPending},
		}
		statusIsSet = true
	case "3": // 已通过
		filterMap[model.ForumPostColumns.PostStatus] = map[string][]any{
			"equalTo": {globalkey.StatusApproved},
		}
		statusIsSet = true
	case "4": // 已驳回
		filterMap[model.ForumPostColumns.PostStatus] = map[string][]any{
			"equalTo": {globalkey.StatusRejected},
		}
		statusIsSet = true
	case "5": // 草稿
		filterMap[model.ForumPostColumns.PostStatus] = map[string][]any{
			"equalTo": {globalkey.StatusDraft},
		}
		statusIsSet = true
	case "6": // 回收站
		filterMap[model.ForumPostColumns.PostStatus] = map[string][]any{
			"equalTo": {globalkey.StatusDeleted},
		}
		statusIsSet = true
	}

	if !restricted {
		page = in.Page
		pageSize = in.PageSize
		withCount = 1
	}

	if in.Title != nil && len(strings.TrimSpace(*in.Title)) >= 2 {
		filterMap[model.ForumPostColumns.PostTitle] = map[string][]any{
			"contain": {strings.TrimSpace(*in.Title)},
		}
	}
	if in.Excerpt != nil && len(strings.TrimSpace(*in.Excerpt)) >= 2 {
		filterMap[model.ForumPostColumns.PostExcerpt] = map[string][]any{
			"equalTo": {strings.TrimSpace(*in.Excerpt)},
		}
	}

	if in.ForumId != nil {
		filterMap[model.ForumPostColumns.ForumID] = map[string][]any{
			"equalTo": {*in.ForumId},
		}
	}
	if in.AuthorId != nil {
		filterMap[model.ForumPostColumns.PostAuthorID] = map[string][]any{
			"equalTo": {*in.AuthorId},
		}
	}
	if in.ParentId != nil {
		filterMap[model.ForumPostColumns.ParentID] = map[string][]any{
			"equalTo": {*in.ParentId},
		}
	}
	if in.TopicId != nil {
		filterMap[model.ForumPostColumns.TopicID] = map[string][]any{
			"equalTo": {*in.TopicId},
		}
	}

	var startTimeIn []any
	var endTimeIn []any
	if in.StartTime != nil && len(strings.TrimSpace(*in.StartTime)) > 0 {
		if startTime, err := modelx.ParseLocalDate(strings.TrimSpace(*in.StartTime)); err == nil {
			startTimeIn = append(startTimeIn, startTime)
		}
	}
	if in.EndTime != nil && len(strings.TrimSpace(*in.EndTime)) > 0 {
		if endTime, err := modelx.ParseLocalDate(strings.TrimSpace(*in.EndTime)); err == nil {
			endTimeIn = append(endTimeIn, endTime)
		}
	}
	createdAtMap := make(map[string][]any)
	if len(startTimeIn) > 0 {
		createdAtMap["moreThanOrEqualTo"] = startTimeIn
	}
	if len(endTimeIn) > 0 {
		createdAtMap["lessThan"] = endTimeIn
	}
	filterMap[model.ForumPostColumns.PostDate] = createdAtMap

	if isAll {
		ret, _, err := l.svcCtx.Post.GetList(l.ctx, model.M{
			"statistic": "1",
		}, 5, filterMap)
		if err != nil {
			// return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			logx.Errorw("BackendGetPostList GetList statistic err", logx.Field("detail", err.Error()))
		} else if len(ret) > 0 {
			statusStatisticMap := make(map[uint32]uint32)
			var total uint16
			for _, v := range ret {
				statusStatisticMap[uint32(v.PostStatus)] = uint32(v.Sort)
				if v.PostStatus != uint8(globalkey.StatusDeleted) {
					total += v.Sort
				}
			}
			statusStatisticMap[uint32(5)] = uint32(total)
			resp.StatusStatisticMap = statusStatisticMap
		}
	}

	if !statusIsSet {
		statusIn := modelx.UniqueInterfaces(stati, 32)
		if len(statusIn) > 0 {
			statusMap["in"] = statusIn
		}
		if in.Status != nil {
			statusMap["equalTo"] = []any{*in.Status}
		}
		filterMap[model.ForumPostColumns.PostStatus] = statusMap
	}

	var orderBy string
	if len(orderBys) > 0 {
		orderBy = strings.Join(orderBys, ",")
	}
	var selectCols string
	var simple bool
	if in.SkipContent != nil && *in.SkipContent > 0 {
		simple = true
		selectCols = strings.Join([]string{
			model.ForumPostColumns.PostID,
			model.ForumPostColumns.TopicID,
			model.ForumPostColumns.ParentID,
			model.ForumPostColumns.PostAuthorID,
			model.ForumPostColumns.PostTitle,
		}, ",")
	}
	res, count, err := l.svcCtx.Post.GetList(l.ctx, model.M{
		"page":     page,
		"pageSize": pageSize,
		"orderBy":  orderBy,
		"select":   selectCols,
	}, withCount, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if restricted {
		count = preCount
	}
	resp.Total = uint64(count)

	if len(res) > 0 {
		if simple {
			for _, v := range res {
				resp.List = append(resp.List, &forum.ForumPostInfo{
					Id:        &v.PostID,
					TopicId:   &v.TopicID,
					ParentId:  &v.ParentID,
					AuthorId:  &v.PostAuthorID,
					PostTitle: &v.PostTitle,
				})
			}

			return resp, nil
		}

		var pids []any
		for _, v := range res {
			pids = append(pids, v.PostID)
		}

		idMetumMap := make(map[uint64][]*model.ForumPostMetum)
		if in.WithMeta != nil && *in.WithMeta > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ForumPostMetumColumns.PostID] = map[string][]any{
				"in": pids,
			}
			subFilterMap[model.ForumPostMetumColumns.MetaKey] = map[string][]any{
				"in": {
					"_thumbnail_id",
					"_thumbnail_url",
					"_photo_count",
					"_view_count",
					"_comment_count",
					"_forward_count",
				},
			}
			list, _, err := l.svcCtx.Post.GetMeta(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					idMetumMap[v.PostID] = append(idMetumMap[v.PostID], v)
				}
			}
		}

		idMediaMap := make(map[uint64][]*forum.MediaInfo)
		if in.WithMedia != nil && *in.WithMedia > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ForumAttachmentRelationshipColumns.ObjectID] = map[string][]any{
				"in": pids,
			}
			subFilterMap[model.ForumAttachmentRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.AttachmentLogTypeForumPost},
			}
			subFilterMap[model.ForumAttachmentRelationshipColumns.AttachmentThumbnail] = map[string][]any{
				"equalTo": {1},
			}
			list, _, err := l.svcCtx.Post.GetMedia(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					idMediaMap[v.ObjectID] = append(idMediaMap[v.ObjectID], &forum.MediaInfo{
						Uid:       &v.AttachmentRelationshipID,
						Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
						Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
						Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
						Uri:       &v.AttachmentSource,
						Url:       &v.AttachmentImageURL,
						AltText:   &v.AttachmentImageAlt,
						MetaData:  &v.AttachmentImageMeta,
					})
				}
			}
		}

		idCardMap := make(map[uint64][]*forum.PostCardInfo)
		if in.WithCard != nil && *in.WithCard > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ForumPostCardColumns.PostID] = map[string][]any{
				"in": pids,
			}
			list, _, err := l.svcCtx.Post.GetCard(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					var objectType string = utils.FormatCardType(int32(v.ObjectType))
					idCardMap[v.PostID] = append(idCardMap[v.PostID], &forum.PostCardInfo{
						Uid:      &v.ID,
						Id:       &v.ObjectID,
						Type:     pointy.GetPointer(objectType),
						LinkUrl:  &v.LinkURL,
						Title:    &v.Title,
						PicUrl:   &v.PicURL,
						Subtitle: &v.Subtitle,
						Label:    &v.Label,
						Price:    &v.Price,
					})
				}
			}
		}

		idTagMap := make(map[uint64][]*forum.TermInfo)
		if in.WithTag != nil && *in.WithTag > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ForumTermRelationshipColumns.ObjectID] = map[string][]any{
				"in": pids,
			}
			subFilterMap[model.ForumTermRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.AttachmentLogTypeForumPost},
			}
			list, _, err := l.svcCtx.ForumTerm.GetRelationshipList(l.ctx, model.M{
				"select": model.ForumTermRelationshipColumns.ObjectID + "," + model.ForumTermRelationshipColumns.TermID,
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				tidMap := make(map[uint64][]uint64) // Map: model.ForumTerm.TermID => []model.ForumPost.PostID
				var tids []any                      // []model.ForumTerm.TermID(已去重)
				for _, v := range list {
					if _, ok := tidMap[v.TermID]; !ok {
						tids = append(tids, v.TermID)
					}
					tidMap[v.TermID] = append(tidMap[v.TermID], v.ObjectID)
				}

				termFilterMap := make(map[string]map[string][]any)
				termFilterMap[model.ForumTermColumns.TermID] = map[string][]any{
					"in": tids,
				}
				terms, _, err := l.svcCtx.ForumTerm.GetList(l.ctx, model.M{}, -1, termFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if len(terms) > 0 {
					for _, term := range terms {
						if oids, ok := tidMap[term.TermID]; ok {
							if term.Taxonomy == uint16(globalkey.TermTaxonomyReviewTag) { // 是香评标签
								oidMap := make(map[uint64]struct{})
								for _, oid := range oids {
									if _, ok := oidMap[oid]; !ok {
										idTagMap[oid] = append(idTagMap[oid], &forum.TermInfo{
											Id:   &term.TermID,
											Name: &term.Name,
											Slug: &term.Slug,
										})
										oidMap[oid] = struct{}{}
									}
								}
							}
						}
					}
				}
			}
		}

		for _, v := range res {
			var meta []*forum.MetaInfo
			var media []*forum.MediaInfo
			if ms, ok := idMetumMap[v.PostID]; ok {
				var mediaId *string
				var mediaUrl *string
				for _, m := range ms {
					key := strings.TrimSpace(m.MetaKey)
					if len(key) > 0 && m.MetaValue.Valid {
						val := m.MetaValue.String
						switch key {
						case "_thumbnail_id":
							mediaId = pointy.GetPointer(val)
						case "_thumbnail_url":
							mediaUrl = pointy.GetPointer(val)
						case "_photo_count":
							meta = append(meta, &forum.MetaInfo{
								Key:   pointy.GetPointer("photoCount"),
								Value: pointy.GetPointer(val),
							})
						case "_view_count":
							meta = append(meta, &forum.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer("viewCount"),
								Value: pointy.GetPointer(val),
							})
						case "_comment_count":
							meta = append(meta, &forum.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer("commentCount"),
								Value: pointy.GetPointer(val),
							})
						case "_forward_count":
							meta = append(meta, &forum.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer("forwardCount"),
								Value: pointy.GetPointer(val),
							})
						default:
							meta = append(meta, &forum.MetaInfo{
								Id:    &m.MetaID,
								Key:   pointy.GetPointer(key),
								Value: pointy.GetPointer(val),
							})
						}
					}
				}
				if mediaUrl != nil {
					media = append(media, &forum.MediaInfo{
						Id:        mediaId,
						Thumbnail: pointy.GetPointer(uint32(1)),
						Url:       mediaUrl,
					})
				}
			}
			if ms, ok := idMediaMap[v.PostID]; ok {
				media = ms[:]
			}

			var cards []*forum.PostCardInfo
			if ms, ok := idCardMap[v.PostID]; ok {
				cards = ms[:]
			}

			var tags []*forum.TermInfo
			if terms, ok := idTagMap[v.PostID]; ok {
				tags = terms[:]
			}

			resp.List = append(resp.List, &forum.ForumPostInfo{
				Id:                  &v.PostID,
				PostTitle:           &v.PostTitle,
				PostContent:         &v.PostContent,
				PostContentFiltered: &v.PostContentFiltered,
				PostExcerpt:         &v.PostExcerpt,
				ForumId:             &v.ForumID,
				TopicId:             &v.TopicID,
				ParentId:            &v.ParentID,
				AuthorId:            &v.PostAuthorID,
				Status:              pointy.GetPointer(uint32(v.PostStatus)),
				CommentStatus:       pointy.GetPointer(uint32(v.CommentStatus)),
				Sort:                pointy.GetPointer(uint32(v.Sort)),
				Sticky:              pointy.GetPointer(uint32(v.Sticky)),
				CreatedAt:           pointy.GetPointer(modelx.FormatTime(v.PostDate, "")),
				UpdatedAt:           pointy.GetPointer(modelx.FormatTime(v.PostModifiedDate, "")),
				DeletedAt:           pointy.GetPointer(modelx.FormatNullDotTime(v.DeletedAt, "")),
				AuthorAvatar:        &v.PostAuthorAvatar,
				AuthorNickname:      &v.PostAuthorNickname,
				AuthorIP:            &v.PostAuthorIP,
				AuthorRankId:        pointy.GetPointer(uint32(v.PostAuthorRankID)),
				AuthorRoleId:        pointy.GetPointer(uint32(v.PostAuthorRoleID)),
				Meta:                meta,
				Media:               media,
				Cards:               cards,
				Tags:                tags,
			})
		}
	}

	return resp, nil
}
